﻿/***************************************************************
 * 开发人员：网魂小兵@http://www.mozlite.com
 * 
 * 使用单键集合，主要用于多线程安全集合对象编程。
 * 
 * 最后修改：2011-10-25
 * ************************************************************/
namespace Mozlite.Collections
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// 单键锁对象集合。
    /// </summary>
    /// <typeparam name="TPrimaryKey">主键类型。</typeparam>
    /// <typeparam name="TValue">返回值类型。</typeparam>
    [Serializable]
    public abstract class SingleKeyLockbox<TPrimaryKey, TValue>
    {
        private readonly IDictionary<int, object> monitorLockbox;
        private readonly object monitorLocker = new object();
        private readonly object singleKeyLocker = new object();

        /// <summary>
        /// 初始化类<see cref="T:Mozlite.Collections.SingleKeyLockbox{TPrimaryKey, TValue}"/>。
        /// </summary>
        /// <param name="size">键数量。</param>
        /// <param name="comparer">主键对比器。</param>
        public SingleKeyLockbox(int size = 0, IEqualityComparer<TPrimaryKey> comparer = null)
        {
            this.PrimaryLockbox = new Dictionary<TPrimaryKey, TValue>(size, comparer);
            this.monitorLockbox = new Dictionary<int, object>(size);
        }

        private TValue GetValue(TPrimaryKey pkey)
        {
            TValue local;
            lock (this.SingleKeyLocker)
            {
                if (this.PrimaryLockbox.TryGetValue(pkey, out local))
                {
                    return local;
                }
            }
            lock (this.GetKeyLock(pkey.GetHashCode()))
            {
                lock (this.SingleKeyLocker)
                {
                    if (this.PrimaryLockbox.TryGetValue(pkey, out local))
                    {
                        return local;
                    }
                }
                if (this.TryLoadByPrimaryKey(pkey, out local))
                {
                    this.SetValueForPrimary(pkey, local);
                }
                return local;
            }
        }
        /// <summary>
        /// 通过值添加锁到列表中。
        /// </summary>
        /// <param name="value">锁对象实例。</param>
        public virtual void Add(TValue value)
        {
            this.SetValueForPrimary(this.GetPrimaryKeyFromValue(value), value);
        }
        /// <summary>
        /// 清理所有键锁列表中的实例。
        /// </summary>
        public virtual void Clear()
        {
            lock (this.SingleKeyLocker)
            {
                this.PrimaryLockbox.Clear();
            }
        }
        /// <summary>
        /// 判断是否包含主键锁。
        /// </summary>
        /// <param name="pkey">主键。</param>
        /// <returns>返回是否包含结果。</returns>
        public bool ContainsPrimaryKey(TPrimaryKey pkey)
        {
            lock (this.SingleKeyLocker)
            {
                return this.PrimaryLockbox.ContainsKey(pkey);
            }
        }
        /// <summary>
        /// 循环执行所有主键锁列表。
        /// </summary>
        /// <param name="action">执行方法，参数为列表锁对象实例。</param>
        public void ForEach(Action<TValue> action)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }
            lock (this.SingleKeyLocker)
            {
                foreach (KeyValuePair<TPrimaryKey, TValue> pair in this.PrimaryLockbox)
                {
                    action(pair.Value);
                }
            }
        }
        /// <summary>
        /// 获取键锁定对象，如果列表中没有存在则创建一个对象返回。
        /// </summary>
        /// <param name="hash">哈希值。</param>
        /// <param name="monitorLocker">锁定对象，当为<c>null</c>时，使用当前对象实例。</param>
        /// <param name="monitorLockbox">锁定对象列表，当为<c>null</c>时，使用当前对象实例。</param>
        /// <returns>返回锁定对象值。</returns>
        protected object GetKeyLock(int hash, object monitorLocker = null, IDictionary<int, object> monitorLockbox = null)
        {
            monitorLocker = monitorLocker ?? this.monitorLocker;
            monitorLockbox = monitorLockbox ?? this.monitorLockbox;
            lock (monitorLocker)
            {
                object locker;
                if (!monitorLockbox.TryGetValue(hash, out locker))
                {
                    locker = new object();
                    monitorLockbox[hash] = locker;
                }
                return locker;
            }
        }
        /// <summary>
        /// 通过值来获取键。
        /// </summary>
        /// <param name="value">当前值。</param>
        /// <returns>返回键实例对象。</returns>
        protected abstract TPrimaryKey GetPrimaryKeyFromValue(TValue value);
        /// <summary>
        /// 通过主键获取值。
        /// </summary>
        /// <param name="pkey">主键。</param>
        /// <returns>返回获得的值。</returns>
        public TValue GetValueByPrimary(TPrimaryKey pkey)
        {
            TValue value = this.GetValue(pkey);
            this.OnValueRetrieved(ref value);
            return value;
        }
        /// <summary>
        /// 当检索到值引发的回调函数。
        /// </summary>
        /// <param name="value">引用值。</param>
        protected virtual void OnValueRetrieved(ref TValue value)
        {
        }
        /// <summary>
        /// 通过值移除键。
        /// </summary>
        /// <param name="value">当前值。</param>
        /// <returns>返回是否移除成功。</returns>
        public virtual bool Remove(TValue value)
        {
            TPrimaryKey primaryKeyFromValue = this.GetPrimaryKeyFromValue(value);
            return this.RemoveByPrimary(primaryKeyFromValue);
        }
        /// <summary>
        /// 通过主键删除值。
        /// </summary>
        /// <param name="pkey">主键。</param>
        /// <returns>返回是否移除成功。</returns>
        public virtual bool RemoveByPrimary(TPrimaryKey pkey)
        {
            lock (this.SingleKeyLocker)
            {
                return this.PrimaryLockbox.Remove(pkey);
            }
        }
        /// <summary>
        /// 通过主键设置值。
        /// </summary>
        /// <param name="pkey">主键。</param>
        /// <param name="value">对应值。</param>
        protected virtual void SetValueForPrimary(TPrimaryKey pkey, TValue value)
        {
            lock (this.SingleKeyLocker)
            {
                this.PrimaryLockbox[pkey] = value;
            }
        }
        /// <summary>
        /// 试图通过主键获取值。
        /// </summary>
        /// <param name="pkey">主键。</param>
        /// <param name="value">返回值。</param>
        /// <returns>返回是否获取成功。</returns>
        protected abstract bool TryLoadByPrimaryKey(TPrimaryKey pkey, out TValue value);
        /// <summary>
        /// 获取多项操作复杂键值锁字典实例对象。
        /// </summary>
        protected IDictionary<TPrimaryKey, TValue> PrimaryLockbox { get; private set; }
        /// <summary>
        /// 获取操作当前<see cref="T:Mozlite.Collections.SingleKeyLockbox{TPrimaryKey, TValue}"/>类的锁定对象实例。
        /// </summary>
        protected object SingleKeyLocker
        {
            get
            {
                return this.singleKeyLocker;
            }
        }
        /// <summary>
        /// 获取锁对象列表中锁对象个数。
        /// </summary>
        public virtual int Count
        {
            get
            {
                return this.PrimaryLockbox.Count;
            }
        }
        /// <summary>
        /// 索引获取值锁对象实例。
        /// </summary>
        /// <param name="pkey">键。</param>
        /// <returns>返回值。</returns>
        public TValue this[TPrimaryKey pkey]
        {
            get
            {
                return this.GetValueByPrimary(pkey);
            }
        }
    }
}
